﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Bridge.Base.Utilities;
using Bridge.UI.Utilities;
using Pathfinder.Base.Constructs;
using Pathfinder.Base.Utilities;

namespace Pathfinder.Base.Importer
{
    public static class StatisticsStatblockImporter
    {
        /// <summary>
        /// Finds the Stats in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="isBase">If true, find base stats; otherwise, find first stats.</param>
        /// <returns>Stats found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input is empty.</exception>
        public static Dictionary<StatType, int> FindStats(string input, bool isBase)
        {
            input.ThrowIfNullOrEmpty("input");

            return FindStats(input, StatRegex, isBase);
        }

        /// <summary>
        /// Finds the Stats in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <param name="isBase">If true, find base stats; otherwise, find first stats.</param>
        /// <returns>Stats found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static Dictionary<StatType, int> FindStats(string input, string regex, bool isBase)
        {
            input.ThrowIfNullOrEmpty("input");
            regex.ThrowIfNullOrEmpty("regex");

            Dictionary<StatType, int> stats = new Dictionary<StatType, int>();
            foreach (StatType statType in Enum.GetValues(typeof(StatType)))
            {
                string match = null;
                string regexInput = string.Format(regex, statType.GetUIDisplayAttribute().ConversionName);
                if (isBase)
                {
                    match = input.FindLastMatch(regexInput);
                }
                else
                {
                    match = input.FindFirstMatch(regexInput);
                }
                stats.Add(statType, int.Parse(match ?? "10"));
            }
            return stats;
        }

        /// <summary>
        /// Finds the BAB in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <returns>BAB found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input is empty.</exception>
        public static int? FindBAB(string input)
        {
            input.ThrowIfNullOrEmpty("input");

            return FindBAB(input, BABRegex);
        }

        /// <summary>
        /// Finds the BAB in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>BAB found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static int? FindBAB(string input, string regex)
        {
            input.ThrowIfNullOrEmpty("input");
            regex.ThrowIfNullOrEmpty("regex");

            string babInfo = input.FindFirstMatch(regex);
            if (babInfo != null)
            {
                bool isPlus = babInfo.Contains("+");
                int bab = int.Parse(babInfo.FindFirstMatch(@"\d+")) * (isPlus ? 1 : -1);
                return bab;
            }
            return null;
        }

        /// <summary>
        /// Finds the Feats in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <returns>Feats found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input is empty.</exception>
        public static List<string> FindFeats(string input)
        {
            input.ThrowIfNullOrEmpty("input");

            return FindFeats(input, FeatRegex);
        }

        /// <summary>
        /// Finds the Feats in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Feats found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static List<string> FindFeats(string input, string regex)
        {
            input.ThrowIfNullOrEmpty("input");
            regex.ThrowIfNullOrEmpty("regex");

            string feats = input.FindFirstMatch(regex);
            feats = string.IsNullOrEmpty(feats) ? null : feats.Replace("\r\n", " ");

            return feats == null ? null : new List<String>(feats.Split(','));
        }

        /// <summary>
        /// Finds the Skills in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Skills found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static List<string> FindSkills(string input)
        {
            input.ThrowIfNullOrEmpty("input");

            return FindSkills(input, SkillRegex);
        }

        /// <summary>
        /// Finds the Skills in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Skills found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static List<string> FindSkills(string input, string regex)
        {
            input.ThrowIfNullOrEmpty("input");
            regex.ThrowIfNullOrEmpty("regex");

            List<string> skills = null;

            string skillList = input.FindFirstMatch(regex);
            if (skillList != null)
            {
                skills = new List<string>(skillList.Split(','));
            }
            return skills;
        }

        /// <summary>
        /// Parses a skill string.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="character">Character to use for stats.</param>
        /// <returns>Skill found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or character is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input is empty.</exception>
        public static Skill FindSkill(string input, PathfinderCharacter character)
        {
            input.ThrowIfNullOrEmpty("input");
            if (character == null) throw new ArgumentNullException("character");

            return FindSkill(input, character, SkillNameRegex);
        }

        /// <summary>
        /// Parses a skill string.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="character">Character to use for stats.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Skill found; null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if input, character or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static Skill FindSkill(string input, PathfinderCharacter character, string regex)
        {
            input.ThrowIfNullOrEmpty("input");
            if (character == null) throw new ArgumentNullException("character");
            regex.ThrowIfNullOrEmpty("regex");

            input = input.Replace("\r\n", " ");
            string skillName = input.FindFirstMatch(regex);
            if (skillName == null) return null;
            int skillAmount = int.Parse(input.FindFirstMatch(@"\d+"));
            string skillSubName = input.FindFirstMatch(@"(?<=\().+?(?=\) )");
            int level = character.TotalHitDice;

            if (!string.IsNullOrEmpty(skillSubName) && skillSubName.StartsWith("any", StringComparison.OrdinalIgnoreCase))
            {
                Random random = new Random();
                ReadOnlyCollection<string> subskills = Skill.GetAllSubskillNames(skillName);
                int randomSkill = random.Next(0, subskills.Count - 1);
                skillSubName = subskills[randomSkill];
            }
            Skill skill = Skill.CreateBaseSkill(skillName, skillSubName);
            skillAmount -= character.TotalStats[skill.BaseStatType].GetBonus();
            if (skillAmount > 4)
            {
                skill.IsClassSkill = true;
                skillAmount -= 3;
            }
            if (skillAmount > 0)
            {
                if (level >= skillAmount)
                {
                    skill.Ranks = level > skillAmount ? skillAmount : level;
                    skillAmount -= level > skillAmount ? skillAmount : level;
                }
                else
                {
                    skill.Ranks = skillAmount;
                    skillAmount = 0;
                }
            }
            if (skill.Ranks > level)
            {
                int removeAmount = skill.Ranks - level;
                skill.Ranks -= removeAmount;
                skill.MiscBonus += removeAmount;
            }
            skill.MiscBonus += skillAmount;

            return skill;
        }

        /// <summary>
        /// Finds the Special Qualities in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Special Qualities found</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static string FindSpecialQualities(string input)
        {
            input.ThrowIfNullOrEmpty("input");

            return FindSpecialQualities(input, SpecialQualitiesRegex);
        }

        /// <summary>
        /// Finds the Special Qualities in a Statblock.
        /// </summary>
        /// <param name="input">Statblock to search.</param>
        /// <param name="regex">Regex to use</param>
        /// <returns>Special Qualities found</returns>
        /// <exception cref="ArgumentNullException">Thrown if input or regex is null.</exception>
        /// <exception cref="ArgumentException">Thrown if input or regex is empty.</exception>
        public static string FindSpecialQualities(string input, string regex)
        {
            input.ThrowIfNullOrEmpty("input");
            regex.ThrowIfNullOrEmpty("regex");

            return input.FindFirstMatch(regex);
        }

        private const string StatRegex = @"(?<={0} )\d+";
        private const string BABRegex = @"(?<=Base Atk ).\d+";
        private const string FeatRegex = @"(?<=Feats ).+(?=Skills)";
        private const string SkillRegex = @"(?<=Skills ).+?((?=Languages)|(?=Racial)|(?=;))";
        private const string SkillNameRegex = @"[a-zA-Z][a-zA-Z ]+?((?= \()|(?= \+)|(?= \-)|(?=$))";
        private const string SpecialQualitiesRegex = @"(?<=SQ ).+?((?=$)|(?=Special))";
    }
}
